package com.guoweizu.mahjong.util;

import java.util.*;

/**
 * 麻将工具类
 * 包括胡牌判定算法实现
 *
 * @author wangxiaodong
 * @since 2020-09-30
 */
public class MahjongUtils {

    private static List mahjongList = new ArrayList();

    static {
        List tong = new LinkedList();
        List tiao = new LinkedList();
        List wan = new LinkedList();

        for(int i=0; i<36; i++){
            tong.add(11 + i%9); // 筒
            tiao.add(31 + i%9); // 条
            wan.add(51 + i%9);  // 万
        }

        mahjongList.addAll(tong);
        mahjongList.addAll(tiao);
        mahjongList.addAll(wan);
    }

    /**
     * 初始化108个麻将牌; 打乱顺序
     *
     * @return
     */
    public static LinkedList shuffleMahjong(){
        LinkedList shuffleList = new LinkedList();
        shuffleList.addAll(mahjongList);
        Collections.shuffle(shuffleList);
        return shuffleList;
    }

    /**
     * 判断某个牌是否可胡
     * @param hold
     * @return
     */
    public static boolean isTingPai(List<Integer> hold, int ting){

        // 不能操作hold，否则会引起后续判断混乱
        List<Integer> operHold = new ArrayList<Integer>();
        operHold.addAll(hold);

        // 将需要判断的牌添加到list中，判断是否可以构成胡牌
        operHold.add(ting);

        //将list转换为数组
        Object[] operHoldArr = operHold.toArray();
        // 重新排序数组
        Arrays.sort(operHoldArr);
        List operHoldList = Arrays.asList(operHoldArr); //将数组转换为list
        // 将abstractList转换为ArrayList,可执行增删操作
        List operHoldArrayList = new ArrayList();
        operHoldArrayList.addAll(operHoldList);

        boolean isHu = isHu(operHoldArrayList);

        return isHu;
    }

    /**
     * 获取所有听牌
     * @param hold13
     * @return
     */
    public static List<Integer> getAllTingPai(List<Integer> hold13){
        List<Integer> pai = new ArrayList<Integer>();
        for(int i=0; i<9; i++){
            pai.add(11 + i);
            pai.add(31 + i);
            pai.add(51 + i);
        }

        printList(pai);

        List<Integer> tingPai = new ArrayList();
        for(int i=0; i<pai.size(); i++){
            if(isTingPai(hold13, pai.get(i))){
                tingPai.add(pai.get(i));
            }
        }
        return tingPai;

    }


    /**
     * 判断是否为胡牌
     * @param hold
     * @return
     */
    public static boolean isHu(List<Integer> hold){
        //拿出对牌
        List checkList = new ArrayList();
        checkList.addAll(hold);

        List<Integer> pairList = getPairs(checkList);
        printList(pairList);
        checkList.addAll(hold);//需要重新加入list，getPair的时候已经全部移除
        for(int i=0; i< pairList.size(); i++){
            int pair = pairList.get(i);
            checkList.remove(new Integer(pair));    //移除对牌，需要移除两次，每次移除一个
            checkList.remove(new Integer(pair));    //移除对牌，需要移除两次，每次移除一个
            printList(checkList);

            List<Integer> operCheckList = new ArrayList();
            operCheckList.addAll(checkList);

            if(operCheckList.size() == 0){  // 等于0,表示只剩下当前牌只剩下对牌，即胡牌
                return true;
            } else {
                boolean is7Pair = true;
                // 判断7对，即剩下的12个牌是6对
                if(12 == operCheckList.size()) {
                    for (int j = 0; j < operCheckList.size() - 1; j = j + 2) {
                        int first = operCheckList.get(j);
                        int second = operCheckList.get(j + 1);
                        if (first != second) { // 如果出现一次不等，则说明不是7对，继续判断后面的情况
                            is7Pair = false;
                        }
                    }
                } else { //剩下的牌少于12张，也不是7对
                    is7Pair = false;
                }

                // 如果是7对，则不用判断后续的牌了，直接表示胡牌
                if(is7Pair){
                    System.out.println("7 pairs!!!");
                    return true;
                }

                //开始判断剩余的牌是否能全部组成AAA或者ABC
                boolean isHuPai = isAAAorABC(operCheckList);

                // 如果是胡牌，则直接返回，否则继续判断另一个对子作为麻将的情况
                if(isHuPai){
                    return true;
                }
            }



            // 重新还原所有的排，准备判断移除下一组对牌
            checkList.removeAll(checkList);
            checkList.addAll(hold);
        }


        return false;

    }

    /**
     * 判定是否存在AAA或者ABC，判定胡牌的关键逻辑
     *
     * @param list
     * @return
     */
    private static boolean isAAAorABC(List<Integer> list){
        List<Integer> operList = new ArrayList();
        operList.addAll(list);
        boolean result;
        if(operList.size() == 0){
            System.out.println("size is 0, all checked");
            return true;
        }else{
            int A = operList.get(0);

            // 先判断AAA：移除三次AAA，最后一次仍为true的话，表示AAA存在，否则不存在，继续查找ABC
            operList.remove(new Integer(A));
            operList.remove(new Integer(A));
            boolean isRemoved = operList.remove(new Integer(A)); // true表示A元素存在，false表示不存在
            if(isRemoved){
                System.out.println("exist AAA:" + A);
                printList(operList);
                result = isAAAorABC(operList); // 存在AAA，继续递归后面的list
            }else{
                // 取ABC
                operList.removeAll(operList);
                operList.addAll(list); // 还原

                int B = A + 1;
                int C = A + 2;
                operList.remove(new Integer(A)); //A肯定存在
                boolean existB = operList.remove(new Integer(B));
                boolean existC = operList.remove(new Integer(C));

                //如果B 和 C都存在，则表示存在ABC，继续递归，否则返回false，因为表示不存在AAA或ABC，不可能为胡牌
                if(existB && existC){
                    System.out.println("exist ABC:" + A + ", " + B + "," + C);
                    printList(operList);
                    result = isAAAorABC(operList);
                }else{
                    return false;
                }

            }

        }

        return result;
    }

    /**
     * 获取对牌
     * @param hold
     * @return
     */
    private static List getPairs(List<Integer> hold){
        List pairList = new ArrayList();

        for(int i=0; i<hold.size(); i++){
            int tmp = hold.get(i);
            hold.remove(i); // 移除之后再判断是否还有相同的元素，如果还有，则说明有对子，移除之后，当前index需要-1,否则不能遍历到下一个元素
            i--;
            if(hold.contains(tmp)){
                if(!pairList.contains(tmp)) {   // 如果不存在，则加入对牌List，可以避免当有三个或者四个相同的的牌时，加入了两个相同的对牌
                    pairList.add(tmp);
                }
            }
        }

        return pairList;
    }

    public static void printArr(Object[] arr){
        for(int i=0; i< arr.length; i++){
            System.out.print(" " + arr[i]);
        }
        System.out.println();
    }

    public static void printList(List list){
        for(int i=0; i<list.size(); i++){
            System.out.print(" " + list.get(i));
        }
        System.out.println();
    }

    /**
     * 判断是否可碰
     * @param list
     * @param current
     * @return
     */
    public static boolean isPeng(List<Integer> list, Integer current){

        int count = 0;
        for(int i=0; i<list.size(); i++){
            Integer e = list.get(i);
            if(current.equals(e)){
                count = count + 1;
            }
        }

        if(count > 1){
            System.out.println("is peng:" + current);
            return true;
        }else{
            return false;
        }
    }

    /**
     * 判断是否可杠
     * @param list
     * @param current
     * @return
     */
    public static boolean isGang(List<Integer> list, Integer current){
        int count = 0;
        for(int i=0; i<list.size(); i++){
            Integer e = list.get(i);
            if(current.equals(e)){
                count = count + 1;
            }
        }

        if(count > 2){
            System.out.println("is gang:" + current);
            return true;
        }else{
            return false;
        }

    }

    /**
     * 判断手里是否有缸牌，起手可能有暗杠牌，碰牌之后可能有巴杠牌？如果有杠牌，但是放弃之后，需要把杠牌放入List中，下次就不用判断了，可以直接显示
     * @return
     */
    public static Set<Integer> getAnBaGang(List<Integer> usingPai, List yiPengPai){
        Set result = new HashSet(); //排重
        if(usingPai.size() < 4){    //如果少于4张牌，直接返回
            return result;
        }

        for(int i=0; i<usingPai.size(); i++){

            // 看是否有暗杠的牌
            int frequency = Collections.frequency(usingPai, usingPai.get(i));
            if(frequency == 4){
                result.add(usingPai.get(i));
            }

            // 检查是否有巴杠的牌
            if(null != yiPengPai && yiPengPai.contains(usingPai.get(i))){
                result.add(usingPai.get(i));
            }
        }

        return result;
    }

    public static void main(String[] args){
        MahjongUtils test = new MahjongUtils();

        //Object[] holdArr13 = test.getRandon(13);
        //Object[] holdArr14 = test.getRandon(14);

        //printArr(holdArr13);
        printList(MahjongUtils.mahjongList);

        List shuffleMahjong = MahjongUtils.shuffleMahjong();
        printList(shuffleMahjong);

        List holdList14 = shuffleMahjong.subList(0,13);
        Object[] holdArr14 = holdList14.toArray();
        Arrays.sort(holdArr14);
        System.out.println("initMahjongSize:" + shuffleMahjong.size());
        //printList(holdArr14);
        printArr(holdArr14);


        List holdList = Arrays.asList(holdArr14);
        List holdArrayList = new ArrayList();
        holdArrayList.addAll(holdList);
        printList(holdList);


        //List pairList = getPairs(holdArrayList);
        //printList(pairList);
        //Integer[] huArr = new Integer[]{11,11,11,12,13,14,15,16,17,18,19,19,19};
        //Integer[] huArr = new Integer[]{12,13,14,15,16,17,18,19,19,19};
        //Integer[] huArr = new Integer[]{13,13,13,15,17,17,17};
        //Integer[] huArr = new Integer[]{12,12,12,13};
        Integer[] huArr = new Integer[]{13,14,14,15,15,58,58};
        //Integer[] huArr = new Integer[]{13};

        List<Integer> huList = Arrays.asList(huArr);
        List<Integer> huArrarList = new ArrayList();
        huArrarList.addAll(huList);
        List<Integer> tingPai = getAllTingPai(huArrarList);
        System.out.println("all ting pai:");
        printList(tingPai);

        //boolean hu = isHu(huArrarList);
        //System.out.println(hu);

        List<Integer> testPengList = new ArrayList<Integer>();
        testPengList.add(11);
        testPengList.add(12);
        testPengList.add(12);
        testPengList.add(11);
        testPengList.add(15);
        testPengList.add(11);
        testPengList.add(11);

        List<Integer> yiPengList = new ArrayList<Integer>();
        yiPengList.add(15);

        //isPeng(testPengList, 11);
        //isGang(testPengList, 11);
        getAnBaGang(testPengList,yiPengList);
    }




}

